#include "wl_transport.h"

#include "wl_mutex.h"
#include "wl_trace.h"

#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
#    include <in_message.h>
#    include <os_api.h>
#    include <sci_types.h>
#    include <socket_api.h>
#    include <socket_types.h>
#elif defined(WL_OS_QUECTEL)
#    include "ql_rtos.h"
#    include "sockets.h"
#else  // default linux
#    include <arpa/inet.h>
#    include <dirent.h>
#    include <errno.h>
#    include <netdb.h>
#    include <netinet/in.h>
#    include <netinet/tcp.h>
#    include <poll.h>
#    include <pthread.h>
#    include <sys/epoll.h>
#    include <sys/ioctl.h>
#    include <sys/prctl.h>
#    include <sys/select.h>
#    include <sys/socket.h>
#    include <sys/types.h>
#    include <time.h>
#    include <unistd.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

typedef struct __wl_transport_message {
    WL_SOCKET socket;
    wl_int32  event;
} wl_transport_message;

#if defined(WL_OS_QUECTEL)
typedef enum {
    WL_TRANSPORT_ADD_CMD,
    WL_TRANSPORT_DEL_CMD,
    WL_TRANSPORT_SET_EVENT_CMD,
    WL_TRANSPORT_POLL_CMD,
    WL_TRANSPORT_END_CMD
} WL_TRANSPORT_CMD;

typedef struct _wl_transport_cmd {
    WL_SOCKET_DESCRIPTOR socket;
    wl_int32             command;
    wl_int32             transport_event;
} wl_transport_cmd;

#    define COMMAND_UDP_PORT 8800
#endif

#define MAX_SOCK_NUM 16

#define START_OF_SOCKFD 1

#define EPOLL_EVENT_NUM 10

#define CHECK_SOCKET(socket) (START_OF_SOCKFD > socket || MAX_SOCK_NUM < socket) ? wl_false : wl_true

typedef struct __wl_transport {
    wl_bool              inuse;
    WL_SOCKET_DESCRIPTOR fd;
    wl_int32             taskid;
    wl_int32             event;
    wl_int32             type;
    wl_sock_callback     callback;
    void*                context;
} wl_transport;

typedef struct {
    wl_bool      initialized;
    wl_bool      exit;
    wl_int32     count;
    wl_transport transports[MAX_SOCK_NUM];
    WL_MUTEX     mutex;
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    BLOCK_ID thread;
#elif defined(WL_OS_QUECTEL)
    ql_task_t thread;
    wl_int32  cmdUdpFd;
#else
    pthread_t thread;
    wl_int32  epollfd;
#endif
} wl_transport_global;

wl_transport_global s_transport_global;

static WL_SOCK_EVENT translate_transport_event_to_socket_event(wl_int32 transport_event);
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
static wl_int32 set_socket_select_event(wl_int32 transport_index, wl_transport* transport);
#elif defined(WL_OS_QUECTEL)
#else  // default linux
static wl_int32 set_socket_select_event(wl_int32 transport_index, wl_transport* transport);
#endif

static WL_SOCK_EVENT translate_transport_event_to_socket_event(wl_int32 transport_event) {
    switch (transport_event) {
        case WL_TRANSPORT_EVENT_READ:
            return WL_SOCK_READ_AVAILABLE;
        case WL_TRANSPORT_EVENT_WRITE:
            return WL_SOCK_WRITE_AVAILABLE;
        case WL_TRANSPORT_EVENT_ACCEPT:
            return WL_SOCK_NEW_CONNECTION;
        case WL_TRANSPORT_EVENT_CONNECT:
            return WL_SOCK_CONNECTION_SUCCESS;
        case WL_TRANSPORT_EVENT_CLOSE:
            return WL_SOCK_CONNECTION_LOST;
        case WL_TRANSPORT_EVENT_LOST:
            return WL_SOCK_CONNECTION_LOST;
        case WL_TRANSPORT_EVENT_EXCEPTION:
            return WL_SOCK_CONNECTION_FAILED;
        default:
            break;
    }
    return WL_SOCK_ERROR_UNKNOWN;
}

#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
static wl_int32 set_socket_select_event(wl_int32 transport_index, wl_transport* transport) {
    wl_uint32 event = AS_READ;
    if (transport->event & WL_TRANSPORT_EVENT_WRITE) {
        event |= AS_WRITE;
    }
    if (transport->event & WL_TRANSPORT_EVENT_CONNECT) {
        event |= AS_CONNECT;
    }
    if (transport->event & WL_TRANSPORT_EVENT_ACCEPT) {
        event |= AS_ACCEPT;
    }
    if (WL_SOCK_STREAM == transport->type) {
        event |= (AS_CLOSE | AS_FULLCLOSED);
    }
    if (0 > sci_sock_asyncselect(transport->fd,
                                 s_transport_global.thread,
                                 AS_READ | AS_WRITE | AS_CONNECT | AS_CLOSE | AS_ACCEPT | AS_FULLCLOSED)) {
        WLLOGW("sci_sock_asyncselect %d fail", transport->fd);
        return -1;
    }
    return 0;
}
#elif defined(WL_OS_QUECTEL)
#else  // default linux
static wl_int32 set_socket_select_event(wl_int32 transport_index, wl_transport* transport) {
    struct epoll_event ev;

    ev.events  = EPOLLET | EPOLLIN | EPOLLHUP | EPOLLERR;
    ev.data.fd = transport_index;

    if (transport->event & WL_TRANSPORT_EVENT_WRITE || transport->event & WL_TRANSPORT_EVENT_CONNECT) {
        ev.events |= EPOLLOUT;
    }
    if (0 > epoll_ctl(s_transport_global.epollfd, EPOLL_CTL_DEL, transport->fd, wl_null)) {
        WLLOGW("epoll_ctl EPOLL_CTL_DEL socket %d fail", transport->fd);
    }
    if (0 > epoll_ctl(s_transport_global.epollfd, EPOLL_CTL_ADD, transport->fd, &ev)) {
        WLLOGW("epoll_ctl socket %d fail", transport->fd);
        return -1;
    }
    // WLLOGT("epoll_ctl sd %d ev %x event %d", transport->fd, ev.events, transport->event);
    return 0;
}
#endif

#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
void transport_main_thread(uint32 argc, void* param) {
    xSignalHeader      signalS = PNULL;
    TCPIP_SOCKET_T     socket;
    wl_int32           i               = 0;
    WL_TRANSPORT_EVENT transport_event = WL_TRANSPORT_EVENT_NONE;
    BLOCK_ID           thread          = SCI_IdentifyThread();

    WLLOGI("transport_main_thread start");
    while (!s_transport_global.exit) {
        signalS = SCI_GetSignal(thread);
        WLLOGT("signal %d", signalS->SignalCode);
        switch (signalS->SignalCode) {
            case SOCKET_READ_EVENT_IND: {
                SOCKET_READ_EVENT_IND_SIG_T* ind = (SOCKET_READ_EVENT_IND_SIG_T*)signalS;
                socket                           = ind->socket_id;
                transport_event                  = WL_TRANSPORT_EVENT_READ;
                break;
            }
            case SOCKET_WRITE_EVENT_IND: {
                SOCKET_WRITE_EVENT_IND_SIG_T* ind = (SOCKET_WRITE_EVENT_IND_SIG_T*)signalS;
                socket                            = ind->socket_id;
                transport_event                   = WL_TRANSPORT_EVENT_WRITE;
                break;
            }
            case SOCKET_CONNECT_EVENT_IND: {
                SOCKET_CONNECT_EVENT_IND_SIG_T* ind = (SOCKET_CONNECT_EVENT_IND_SIG_T*)signalS;
                socket                              = ind->socket_id;
                transport_event                     = WL_TRANSPORT_EVENT_CONNECT;
                break;
            }
            case SOCKET_CONNECTION_CLOSE_EVENT_IND: {
                SOCKET_CONNECTION_CLOSE_EVENT_IND_SIG_T* ind = (SOCKET_CONNECTION_CLOSE_EVENT_IND_SIG_T*)signalS;
                socket                                       = ind->socket_id;
                transport_event                              = WL_TRANSPORT_EVENT_CLOSE;
                break;
            }
            case SOCKET_ACCEPT_EVENT_IND: {
                SOCKET_ACCEPT_EVENT_IND_SIG_T* ind = (SOCKET_ACCEPT_EVENT_IND_SIG_T*)signalS;
                socket                             = ind->socket_id;
                transport_event                    = WL_TRANSPORT_EVENT_ACCEPT;
                break;
            }
            case SOCKET_FULL_CLOSED_EVENT_IND: {
                SOCKET_FULL_CLOSED_EVENT_IND_SIG_T* ind = (SOCKET_FULL_CLOSED_EVENT_IND_SIG_T*)signalS;
                socket                                  = ind->socket_id;
                transport_event                         = WL_TRANSPORT_EVENT_CLOSE;
                break;
            }
            default:
                WLLOGW("unknown signal");
                break;
        }

        wl_mutex_acquire(&s_transport_global.mutex);
        for (i = 0; i < MAX_SOCK_NUM; i++) {
            if (s_transport_global.transports[i].inuse && WL_INVALID_SOCKET != s_transport_global.transports[i].fd
                && s_transport_global.transports[i].fd == socket) {
                if (transport_event) {
                    wl_transport_message transport_message;
                    wl_task_message      message;
                    memset(&transport_message, 0, sizeof(wl_transport_message));
                    memset(&message, 0, sizeof(wl_task_message));
                    transport_message.event  = transport_event;
                    transport_message.socket = i + START_OF_SOCKFD;
                    message.type             = WL_TASK_MSG_SOCKET;
                    memcpy(&message.data, &transport_message, sizeof(wl_transport_message));
                    message.datalen = sizeof(wl_transport_message);
                    wl_send_task_message(s_transport_global.transports[i].taskid, &message);
                    WLLOGT("send taskid %d socket %d event %d",
                           s_transport_global.transports[i].taskid,
                           transport_message.socket,
                           transport_event);
                }
                break;
            }
        }
        wl_mutex_release(&s_transport_global.mutex);

        SCI_FREE(signalS);
    }
    SCI_ThreadExit();
}
#elif defined(WL_OS_QUECTEL)
static wl_int32 wl_transport_send_cmd(WL_SOCKET_DESCRIPTOR socket, wl_int32 transport_cmd, wl_int32 event) {
    if (s_transport_global.cmdUdpFd >= 0) {
        struct sockaddr_in addr_serv;
        wl_transport_cmd   cmd;
        int                sendBytes;
        addr_serv.sin_family      = AF_INET;
        addr_serv.sin_port        = htons(COMMAND_UDP_PORT);
        addr_serv.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

        cmd.command         = transport_cmd;
        cmd.socket          = socket;
        cmd.transport_event = event;
        WLLOGI("send command:%d with event:%d", transport_cmd, event);
        sendBytes = sendto(s_transport_global.cmdUdpFd,
                           (void*)&cmd,
                           sizeof(wl_transport_cmd),
                           0,
                           (struct sockaddr*)&addr_serv,
                           sizeof(addr_serv));
        if (sendBytes >= 0) {
            return 0;
        }
    }

    return -1;
}

void transport_main_thread(void* param) {
    wl_transport*      transport = wl_null;
    WL_TRANSPORT_EVENT transport_event;
    int                i;
    fd_set             rset;
    fd_set             wset;
    // struct timeval     timeout;
    int                ret   = 0;
    int                maxfd = 0;

    while (!s_transport_global.exit) {
        FD_ZERO(&rset);
        FD_ZERO(&wset);

        FD_SET(s_transport_global.cmdUdpFd, &rset);
        maxfd = s_transport_global.cmdUdpFd;

        wl_mutex_acquire(&s_transport_global.mutex);
        for (i = 0; i < MAX_SOCK_NUM; i++) {
            if (s_transport_global.transports[i].inuse) {
                if (s_transport_global.transports[i].event & WL_TRANSPORT_EVENT_WRITE
                    || s_transport_global.transports[i].event & WL_TRANSPORT_EVENT_CONNECT) {
                    FD_SET(s_transport_global.transports[i].fd, &wset);
                    if (maxfd < s_transport_global.transports[i].fd) {
                        maxfd = s_transport_global.transports[i].fd;
                    }
                }

                if (s_transport_global.transports[i].event & WL_TRANSPORT_EVENT_READ
                    || s_transport_global.transports[i].event & WL_TRANSPORT_EVENT_ACCEPT
                    || s_transport_global.transports[i].event & WL_TRANSPORT_EVENT_CONNECT) {
                    FD_SET(s_transport_global.transports[i].fd, &rset);
                    if (maxfd < s_transport_global.transports[i].fd) {
                        maxfd = s_transport_global.transports[i].fd;
                    }
                }
            }
        }
        wl_mutex_release(&s_transport_global.mutex);

        // WLLOGD("wait for maxfd %d event:%d %d", maxfd, *(int*)&rset, *(int*)&wset);
        // timeout.tv_sec  = 19;
        // timeout.tv_usec = 1000000;
        ret             = select(maxfd + 1, &rset, &wset, NULL, NULL);

        if (ret < 0) {
            WLLOGI("!!!!!!!!!!!!!!!!! select is error:%d", errno);
        } else if (ret > 0) {
            if (FD_ISSET(s_transport_global.cmdUdpFd, &rset)) {
                wl_transport_cmd   cmd;
                struct sockaddr_in addr_serv;
                int                len, recvSize;
                recvSize = recvfrom(s_transport_global.cmdUdpFd,
                                    &cmd,
                                    sizeof(wl_transport_cmd),
                                    0,
                                    (struct sockaddr*)&addr_serv,
                                    (socklen_t*)&len);
                if (recvSize > 0) {
                    switch (cmd.command) {
                        case WL_TRANSPORT_ADD_CMD: {
                        } break;
                        case WL_TRANSPORT_DEL_CMD: {
                            if (cmd.socket >= 0) {
                                struct linger linger = {0};
                                linger.l_onoff       = 1;
                                linger.l_linger      = 10;

                                setsockopt(cmd.socket, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
                                setsockopt(cmd.socket,
                                           IPPROTO_TCP,
                                           TCP_CLOSE_TIMEROUT,
                                           &linger.l_linger,
                                           sizeof(linger.l_linger));
                                close(cmd.socket);
                            }
                        } break;
                        case WL_TRANSPORT_POLL_CMD: {
                            wl_transport_poll();
                            break;
                        }
                        case WL_TRANSPORT_SET_EVENT_CMD: {
                        } break;

                        case WL_TRANSPORT_END_CMD: {
                            wl_int32 index;
                            wl_mutex_acquire(&s_transport_global.mutex);
                            for (index = 0; index < MAX_SOCK_NUM; index++) {
                                if (s_transport_global.transports[index].inuse) {
                                    close(s_transport_global.transports[index].fd);
                                    memset(&s_transport_global.transports[index], 0, sizeof(wl_transport));
                                }
                            }

                            close(s_transport_global.cmdUdpFd);
                            wl_mutex_release(&s_transport_global.mutex);
                        } break;
                    }
                }
                continue;
            }

            for (i = 0; i < MAX_SOCK_NUM; i++) {
                transport_event = WL_TRANSPORT_EVENT_NONE;
                if (s_transport_global.transports[i].inuse) {
                    wl_mutex_acquire(&s_transport_global.mutex);
                    if (s_transport_global.transports[i].inuse) {
                        transport = &s_transport_global.transports[i];
                        if (FD_ISSET(transport->fd, &rset)) {
                            if (FD_ISSET(transport->fd, &wset)) {
                                int       sock_error;
                                socklen_t len;
                                int       ret = getsockopt(transport->fd, SOL_SOCKET, SO_ERROR, &sock_error, &len);

                                WLLOGI("socket is connecting, ret:%d sock_err:%d", ret, sock_error);

                                if (transport->event & WL_TRANSPORT_EVENT_CONNECT) {
                                    transport->event &= ~WL_TRANSPORT_EVENT_CONNECT;
                                    if (ret == 0 && sock_error == 0) {
                                        transport_event = WL_TRANSPORT_EVENT_CONNECT;
                                    } else {
                                        transport_event = WL_TRANSPORT_EVENT_EXCEPTION;
                                    }
                                }
                            } else {
                                WLLOGD("socket %d event %d read set", transport->fd, transport->event);
                                if (transport->event & WL_TRANSPORT_EVENT_CONNECT) {
                                    transport->event &= ~WL_TRANSPORT_EVENT_CONNECT;
                                    transport_event = WL_TRANSPORT_EVENT_EXCEPTION;
                                } else if (transport->event & WL_TRANSPORT_EVENT_ACCEPT) {
                                    transport->event &= ~WL_TRANSPORT_EVENT_ACCEPT;
                                    transport_event = WL_TRANSPORT_EVENT_ACCEPT;
                                }
                            }

                            if (transport->event & WL_TRANSPORT_EVENT_READ) {
                                transport->event &= ~WL_TRANSPORT_EVENT_READ;
                                transport_event = WL_TRANSPORT_EVENT_READ;
                            }
                        } else if (FD_ISSET(transport->fd, &wset)) {
                            if (transport->event & WL_TRANSPORT_EVENT_CONNECT) {
                                transport->event &= ~WL_TRANSPORT_EVENT_CONNECT;
                                transport_event = WL_TRANSPORT_EVENT_CONNECT;
                            }
                        } else {
                            if (transport->event & WL_TRANSPORT_EVENT_CONNECT) {
                                transport->event &= ~WL_TRANSPORT_EVENT_CONNECT;
                                transport_event = WL_TRANSPORT_EVENT_EXCEPTION;
                            }
                        }

                        if (FD_ISSET(transport->fd, &wset)) {
                            if (transport->event & WL_TRANSPORT_EVENT_WRITE) {
                                transport->event &= ~WL_TRANSPORT_EVENT_WRITE;
                                transport_event = WL_TRANSPORT_EVENT_WRITE;
                            }
                        }
                    }
                    wl_mutex_release(&s_transport_global.mutex);
                }
                if (transport_event) {
                    wl_transport_message transport_message;
                    wl_task_message      message;
                    memset(&transport_message, 0, sizeof(wl_transport_message));
                    memset(&message, 0, sizeof(wl_task_message));
                    transport_message.event  = transport_event;
                    transport_message.socket = i + START_OF_SOCKFD;
                    message.type             = WL_TASK_MSG_SOCKET;
                    memcpy(&message.data, &transport_message, sizeof(wl_transport_message));
                    message.datalen = sizeof(wl_transport_message);
                    wl_send_task_message(transport->taskid, &message);
                }
            }

        } else {
            ql_rtos_task_sleep_ms(100);
        }
    }

    ql_rtos_task_delete(s_transport_global.thread);
    wl_mutex_delete(&s_transport_global.mutex);
}
#else
void* transport_main_thread(void* param) {
    wl_transport*      transport = wl_null;
    struct epoll_event events[EPOLL_EVENT_NUM];
    wl_int32           nfds = 0;
    wl_int32           i    = 0;
    WL_TRANSPORT_EVENT transport_event;

    prctl(PR_SET_NAME, "transport_main_thread");

    UNUSED(param);
    s_transport_global.epollfd = epoll_create(MAX_SOCK_NUM);
    WLLOGT("transport start EPOLLIN %d EPOLLOUT %d EPOLLHUP %d EPOLLERR %d", EPOLLIN, EPOLLOUT, EPOLLHUP, EPOLLERR);
    while (!s_transport_global.exit) {
        nfds = epoll_wait(s_transport_global.epollfd, events, EPOLL_EVENT_NUM, -1);
        for (i = 0; i < nfds; i++) {
            wl_mutex_acquire(&s_transport_global.mutex);
            transport_event = WL_TRANSPORT_EVENT_NONE;
            transport       = &s_transport_global.transports[events[i].data.fd];
            WLLOGT("socket %d events %d transport event %x",
                   events[i].data.fd + START_OF_SOCKFD,
                   events[i].events,
                   transport->event);
            if ((EPOLLHUP & events[i].events) || (EPOLLERR & events[i].events)) {
                if (transport->event & WL_TRANSPORT_EVENT_CONNECT) {
                    transport_event = WL_TRANSPORT_EVENT_LOST;
                    transport->event &= ~WL_TRANSPORT_EVENT_CONNECT;
                } else {
                    transport_event = WL_TRANSPORT_EVENT_CLOSE;
                }
                epoll_ctl(s_transport_global.epollfd, EPOLL_CTL_DEL, transport->fd, wl_null);
            } else if (EPOLLIN & events[i].events) {
                if (transport->event & WL_TRANSPORT_EVENT_ACCEPT) {
                    transport_event = WL_TRANSPORT_EVENT_ACCEPT;
                    transport->event &= ~WL_TRANSPORT_EVENT_ACCEPT;
                } else {
                    transport_event = WL_TRANSPORT_EVENT_READ;
                }
            } else if (EPOLLOUT & events[i].events) {
                // 连接成功,添加可读事件监听
                if (transport->event & WL_TRANSPORT_EVENT_CONNECT) {
                    struct epoll_event ev;

                    transport_event = WL_TRANSPORT_EVENT_CONNECT;
                    transport->event &= ~WL_TRANSPORT_EVENT_CONNECT;

                    ev.events  = EPOLLET | EPOLLIN | EPOLLHUP | EPOLLERR;
                    ev.data.fd = events[i].data.fd;
                    epoll_ctl(s_transport_global.epollfd, EPOLL_CTL_DEL, transport->fd, wl_null);
                    if (0 > epoll_ctl(s_transport_global.epollfd, EPOLL_CTL_ADD, transport->fd, &ev)) {
                        WLLOGW("epoll_ctl sd %d EPOLL_CTL_ADD fd %d fail", s_transport_global.epollfd, transport->fd);
                        transport_event = WL_TRANSPORT_EVENT_EXCEPTION;
                    }
                } else {
                    transport_event = WL_TRANSPORT_EVENT_WRITE;
                }
            } else {
                WLLOGT("invalid events %d", events[i].events);
            }
            wl_mutex_release(&s_transport_global.mutex);

            if (transport_event) {
                wl_transport_message transport_message;
                wl_task_message      message;
                memset(&transport_message, 0, sizeof(wl_transport_message));
                memset(&message, 0, sizeof(wl_task_message));
                transport_message.event  = transport_event;
                transport_message.socket = events[i].data.fd + START_OF_SOCKFD;
                message.type             = WL_TASK_MSG_SOCKET;
                memcpy(&message.data, &transport_message, sizeof(wl_transport_message));
                message.datalen = sizeof(wl_transport_message);
                wl_send_task_message(transport->taskid, &message);
                // WLLOGT(
                //     "send taskid %d socket %d event %d", transport->taskid, transport_message.socket,
                //     transport_event);
            }
        }
    }
    close(s_transport_global.epollfd);
    pthread_exit(wl_null);
    WLLOGT("transport exit");
    return wl_null;
}
#endif

wl_bool wl_transport_set_socket_event(WL_SOCKET socket, wl_int32 event) {
    if (CHECK_SOCKET(socket)) {
        wl_transport* transport = wl_null;
        wl_mutex_acquire(&s_transport_global.mutex);
        transport = &s_transport_global.transports[socket - START_OF_SOCKFD];
        if (transport->inuse && WL_INVALID_SOCKET != transport->fd) {
            transport->event |= event;
#if defined(WL_OS_QUECTEL)
            wl_transport_send_cmd(transport->fd, WL_TRANSPORT_SET_EVENT_CMD, event);
#else
            set_socket_select_event(socket - START_OF_SOCKFD, transport);
#endif
        }
        wl_mutex_release(&s_transport_global.mutex);
        return wl_true;
    } else {
        return wl_false;
    }
}

WL_SOCKET_DESCRIPTOR wl_transport_get_socket_descriptor(WL_SOCKET socket) {
    WL_SOCKET_DESCRIPTOR sd = WL_INVALID_SOCKET;
    if (CHECK_SOCKET(socket)) {
        wl_transport* transport = wl_null;
        wl_mutex_acquire(&s_transport_global.mutex);
        transport = &s_transport_global.transports[socket - START_OF_SOCKFD];
        if (transport->inuse && WL_INVALID_SOCKET != transport->fd) {
            sd = transport->fd;
        }
        wl_mutex_release(&s_transport_global.mutex);
    }
    return sd;
}

wl_int32 wl_transport_get_socket_taskid(WL_SOCKET socket) {
    if (CHECK_SOCKET(socket)) {
        return s_transport_global.transports[socket - START_OF_SOCKFD].taskid;
    }
    return -1;
}

wl_int32 wl_transport_set_socket_callback(WL_SOCKET socket, wl_sock_callback callback, void* context) {
    if (CHECK_SOCKET(socket)) {
        wl_transport* transport = wl_null;
        wl_mutex_acquire(&s_transport_global.mutex);
        transport = &s_transport_global.transports[socket - START_OF_SOCKFD];
        // WLLOGT("socket %d callback %p context %p inuse %d fd %d",
        //        socket,
        //        callback,
        //        context,
        //        transport->inuse,
        //        transport->fd);
        if (transport->inuse && WL_INVALID_SOCKET != transport->fd) {
            transport->callback = callback;
            transport->context  = context;
        }
        wl_mutex_release(&s_transport_global.mutex);
        return 0;
    } else {
        WLLOGW("socket %d invalid", socket);
        return -1;
    }
}

wl_int32 wl_transport_init(void) {
    WLLOGI("wl_transport_init");
    if (!s_transport_global.initialized) {
        s_transport_global.exit = wl_false;

        if (0 != wl_mutex_create("wlsdk_transport", &s_transport_global.mutex)) {
            WLLOGW("wl_mutex_create fail");
            return -1;
        }
        // 创建线程
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
        s_transport_global.thread = SCI_CreateThread("wl_trans",
                                                     "wl_trans_queue",
                                                     transport_main_thread,
                                                     0,
                                                     wl_null,
                                                     8 * 1024,
                                                     30,
                                                     20,
                                                     SCI_PREEMPT,
                                                     SCI_AUTO_START);
        if (SCI_INVALID_BLOCK_ID == s_transport_global.thread) {
            wl_mutex_delete(s_transport_global.mutex);
            WLLOGW("SCI_CreateThread fail");
            return -1;
        }
#elif defined(WL_OS_QUECTEL)
        struct sockaddr_in addr_serv;

        addr_serv.sin_family      = AF_INET;
        addr_serv.sin_port        = htons(COMMAND_UDP_PORT);
        addr_serv.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

        // 创建一个命令用的udp socket，用来通知select的线程进行一些操作
        s_transport_global.cmdUdpFd = socket(AF_INET, SOCK_DGRAM, 0);
        if (s_transport_global.cmdUdpFd < 0) {
            WLLOGW("command udp socket create failed");
            wl_mutex_delete(&s_transport_global.mutex);
            return -1;
        }

        if (bind(s_transport_global.cmdUdpFd, (struct sockaddr*)&addr_serv, sizeof(addr_serv)) < 0) {
            WLLOGW("command udp socket binding failed");
            close(s_transport_global.cmdUdpFd);
            wl_mutex_delete(&s_transport_global.mutex);
            return -1;
        }

        // 创建线程
        if (0
            != ql_rtos_task_create(
                &s_transport_global.thread, 8 * 1024, 99, "wl_trans", transport_main_thread, wl_null)) {
            wl_mutex_delete(&s_transport_global.mutex);
        }
#else  // default linux
        pthread_attr_t attr;
        if (0 != pthread_attr_init(&attr)) {
            WLLOGW("pthread_attr_init fail");
            wl_mutex_delete(&s_transport_global.mutex);
            return -1;
        }

        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        if (0 != pthread_create(&s_transport_global.thread, &attr, transport_main_thread, wl_null)) {
            pthread_attr_destroy(&attr);
            wl_mutex_delete(&s_transport_global.mutex);
            WLLOGW("pthread_create fail");
            return -1;
        }
        pthread_attr_destroy(&attr);
#endif
        s_transport_global.initialized = wl_true;
        WLLOGT("wl_transport_init success");
    }
    return 0;
}

void wl_transport_end(void) {
    if (s_transport_global.initialized) {
        wl_int32 index           = 0;
        s_transport_global.exit  = wl_true;
        s_transport_global.count = 0;
        wl_mutex_acquire(&s_transport_global.mutex);

        for (index = 0; index < MAX_SOCK_NUM; index++) {
            if (s_transport_global.transports[index].inuse) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
                sci_sock_socketclose(s_transport_global.transports[index].fd);
#elif defined(WL_OS_QUECTEL)
#else  // default linux
                close(s_transport_global.transports[index].fd);
#endif
                memset(&s_transport_global.transports[index], 0, sizeof(wl_transport));
            }
        }
        wl_mutex_release(&s_transport_global.mutex);
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
        SCI_DeleteThread(s_transport_global.thread);
#elif defined(WL_OS_QUECTEL)
        wl_transport_send_cmd(0, WL_TRANSPORT_END_CMD, 0);
#else
        pthread_join(s_transport_global.thread, wl_null);
#endif
        wl_mutex_delete(&s_transport_global.mutex);
        s_transport_global.initialized = wl_false;
    }
}

WL_SOCKET wl_transport_add_socket(WL_SOCKET_DESCRIPTOR sd, wl_int32 type, wl_int32 taskid) {
    wl_int32 index = 0;

    wl_mutex_acquire(&s_transport_global.mutex);
    for (index = 0; index < MAX_SOCK_NUM; index++) {
        if (!s_transport_global.transports[index].inuse) {
            WL_SOCKET     socket    = index + START_OF_SOCKFD;
            wl_transport* transport = &s_transport_global.transports[index];
            memset(transport, 0, sizeof(wl_transport));
            transport->fd     = sd;
            transport->taskid = taskid;
            transport->inuse  = wl_true;
            transport->type   = type;
            transport->event  = WL_TRANSPORT_EVENT_NONE;

            s_transport_global.count++;
            wl_mutex_release(&s_transport_global.mutex);

#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
#elif defined(WL_OS_QUECTEL)
            // 通知select 线程， 增加一个socket
            wl_transport_send_cmd(transport->fd, WL_TRANSPORT_ADD_CMD, 0);
#else
#endif
            return socket;
        }
    }
    wl_mutex_release(&s_transport_global.mutex);
    WLLOGW("add task %d sd %d fail", sd, taskid);
    return WL_INVALID_SOCKET;
}

wl_int32 wl_transport_delete_socket(WL_SOCKET socket) {
    if (CHECK_SOCKET(socket)) {
        wl_transport* transport = wl_null;
        wl_mutex_acquire(&s_transport_global.mutex);
        transport = &s_transport_global.transports[socket - START_OF_SOCKFD];
        if (transport->inuse && WL_INVALID_SOCKET != transport->fd) {
            s_transport_global.count--;
            transport->inuse    = wl_false;
            transport->taskid   = 0;
            transport->callback = wl_null;
            transport->context  = wl_null;
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
#elif defined(WL_OS_QUECTEL)
            // 通知select 线程删除一个socket
            wl_transport_send_cmd(transport->fd, WL_TRANSPORT_DEL_CMD, 0);
#else
            if (0 > epoll_ctl(s_transport_global.epollfd, EPOLL_CTL_DEL, transport->fd, wl_null)) {
                WLLOGW("epoll_ctl EPOLL_CTL_DEL socket %d fail", transport->fd);
            }
#endif
            transport->fd = WL_INVALID_SOCKET;
        }
        wl_mutex_release(&s_transport_global.mutex);
        return 0;
    } else {
        return -1;
    }
}

wl_int32 wl_transport_process_message(wl_task_message* message) {
    wl_transport_message transport_message;
    wl_sock_callback     callback = wl_null;
    void*                context  = wl_null;
    if (wl_null == message || sizeof(wl_transport_message) != message->datalen) {
        WLLOGW("message is null");
        return -1;
    }
    memcpy(&transport_message, &(message->data), message->datalen);
    if (CHECK_SOCKET(transport_message.socket)) {
        wl_transport* transport = wl_null;
        wl_mutex_acquire(&s_transport_global.mutex);
        transport = &s_transport_global.transports[transport_message.socket - START_OF_SOCKFD];
        callback  = transport->callback;
        context   = transport->context;
        wl_mutex_release(&s_transport_global.mutex);

        if (callback) {
            WL_SOCK_EVENT socket_event = translate_transport_event_to_socket_event(transport_message.event);
            WLLOGT("socket: %d event: %d socket_event %d",
                   transport_message.socket,
                   transport_message.event,
                   socket_event);
            callback(transport_message.socket, socket_event, context);
        } else {
            WLLOGW("socket: %d callback is null", transport_message.socket);
        }
    } else {
        WLLOGW("invalid socket %d", transport_message.socket);
    }

    return 0;
}

wl_int32 wl_transport_set_poll(void) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
#elif defined(WL_OS_QUECTEL)
    wl_transport_send_cmd(0, WL_TRANSPORT_POLL_CMD, 0);
#else
#endif
}

wl_int32 wl_transport_poll(void) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
#elif defined(WL_OS_QUECTEL)
    int i;
    WLLOGD("transport poll");
    for (i = 0; i < MAX_SOCK_NUM; i++) {
        WL_TRANSPORT_EVENT transport_event = WL_TRANSPORT_EVENT_NONE;
        int32              taskid          = 0;
        if (s_transport_global.transports[i].inuse) {
            wl_mutex_acquire(&s_transport_global.mutex);
            if (s_transport_global.transports[i].inuse) {
                if (s_transport_global.transports[i].event & WL_TRANSPORT_EVENT_READ) {
                    transport_event = WL_TRANSPORT_EVENT_READ;
                    taskid          = s_transport_global.transports[i].taskid;
                }
            }
            wl_mutex_release(&s_transport_global.mutex);
        }
        if (transport_event) {
            wl_transport_message transport_message;
            wl_task_message      message;
            memset(&transport_message, 0, sizeof(wl_transport_message));
            memset(&message, 0, sizeof(wl_task_message));
            transport_message.event  = transport_event;
            transport_message.socket = i + START_OF_SOCKFD;
            message.type             = WL_TASK_MSG_SOCKET;
            memcpy(&message.data, &transport_message, sizeof(wl_transport_message));
            message.datalen = sizeof(wl_transport_message);
            WLLOGD("transport socket %d", transport_message.socket);
            wl_send_task_message(taskid, &message);
        }
    }
#else
#endif
}

#ifdef __cplusplus
}
#endif /* __cplusplus */
